package org.zjt.mock.demo.mock.config;

import io.reactivex.Flowable;
import io.reactivex.Maybe;
import io.reactivex.Observable;
import io.reactivex.Single;
import org.springframework.core.MethodParameter;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.async.DeferredResult;
import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.method.support.AsyncHandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;

import java.util.Objects;
import java.util.concurrent.Callable;

/**
 * Description:
 *
 * @author juntao.zhang
 * Date: 2018-06-21 下午8:37
 * @see
 */
public class CustomizedAsyncHandlerMethodReturnValueHandler implements AsyncHandlerMethodReturnValueHandler {
    /**
     * 是否支持异步
     * @param returnValue
     * @param returnType
     * @return
     */
    @Override
    public boolean isAsyncReturnValue(Object returnValue, MethodParameter returnType) {

        return Objects.equals(returnType.getParameterType(), Observable.class) || Objects.equals(returnType.getParameterType(), Flowable.class)
                || Objects.equals(returnType.getParameterType(), Maybe.class) || Objects.equals(returnType.getParameterType(), Single.class);
    }

    /**
     * 支持对象类型
     * @param returnType
     * @return
     */
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return Objects.equals(returnType.getParameterType(), Observable.class) || Objects.equals(returnType.getParameterType(), Flowable.class)
                || Objects.equals(returnType.getParameterType(), Maybe.class) || Objects.equals(returnType.getParameterType(), Single.class);
    }

    @Override
    public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

        if (returnValue == null) {
            mavContainer.setRequestHandled(true);
            return;
        }
        if (returnValue instanceof Flowable) {
            final Flowable<?> flowable = Flowable.class.cast(returnValue);

            WebAsyncUtils.getAsyncManager(webRequest).startDeferredResultProcessing(new FlowableAdapter<>(flowable), mavContainer);
        } else if (returnValue instanceof Single) {
            final Single<?> single = Single.class.cast(returnValue);

            WebAsyncUtils.getAsyncManager(webRequest).startDeferredResultProcessing(new SingleAdapter<>(single), mavContainer);
        }

    }


    /**
     * 在构造器中定义了数据流向：
     *      最后流入setResult中和setErrorResult中。
     * @param <T>
     */
    public class SingleAdapter<T> extends DeferredResult<T> {

        private SingleAdapter(Single<T> singleSource) {
            //noinspection ResultOfMethodCallIgnored
            singleSource.subscribe(this::setResult, this::setErrorResult);
        }
    }

    public class FlowableAdapter<T> extends DeferredResult<T> {

        private FlowableAdapter(Flowable<T> flowable) {
            //noinspection ResultOfMethodCallIgnored
            flowable.firstOrError().subscribe(this::setResult, this::setErrorResult);
        }
    }

}
